package com.smartao.core.license;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.StringReader;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;

import org.apache.commons.codec.binary.Base64;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.smartao.core.BaseContext;
import com.smartao.core.Environment;
import com.smartao.core.Version;

public class LicenseManager {

	private PublicKey publicKey;
	private PrivateKey privateKey;

	private static final Logger logger = LoggerFactory.getLogger(LicenseManager.class);

	public LicenseManager() {
		try {
			InputStream pub_key = getClass().getResourceAsStream(Environment.KEY_URL);
			ObjectInputStream ois_pub_key = new ObjectInputStream(pub_key);
			publicKey = (PublicKey) ois_pub_key.readObject();
			ois_pub_key.close();
		} catch (Exception e) {
			logger.error("LicenseManager init failed!", e);
		}
	}

	/**
	 * 解析license
	 * @param licenseXml
	 * @return
	 */
	public License parseLicense(String licenseString) {
		logger.info("Parsing license...");
		Base64 base64 = new Base64();
		License license = new License();
		SAXReader reader = new SAXReader();
		try {
			String licenseXml = new String(base64.decode(licenseString.getBytes()), "UTF8");
			Document document = reader.read(new StringReader(licenseXml));
			Element root = document.getRootElement();
			String productName = root.elementText("productName");
			String productVersion = root.elementText("productVersion");
			String serverID = root.elementText("serverID");
			String organization = root.elementText("organization");
			int maxLicense = Integer.parseInt(root.elementText("maxLicense"));
			String expireDate = root.elementText("expireDate");
			LicenseType licenseType = LicenseType.valueOf(root.elementText("licenseType"));
			String signature = root.elementText("signature");
			license.setOrganization(organization);
			license.setMaxLicense(maxLicense);
			license.setExpireDate(expireDate);
			license.setLicenseType(licenseType);
			license.setSignature(signature);
			license.setProductName(productName);
			license.setProductVersion(productVersion);
			license.setServerID(serverID);
			license.setVerified(verifySignature(license));
		} catch (Exception e) {
			license = null;
			logger.error("ParseLicense license failed!", e);
		}
		return license;
	}

	public License loadLicense(String filePath) {
		logger.info("Loadding license from:", filePath);
		File file = new File(filePath);
		if (file.exists()) {
			return loadLicense(file);
		} else {
			return null;
		}
	}

	public License loadLicense(File licenseFile) {
		if (!licenseFile.exists()) {
			logger.info("License file not found! Use default Evaluation license.");
			return getEvaluationLicense();
		}

		StringBuffer buffer = new StringBuffer();
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(licenseFile)));
			String line = reader.readLine();
			while (line != null) {
				buffer.append(line);
				line = reader.readLine();
			}
			reader.close();
		} catch (IOException e) {
			logger.error("Load license failed! Use default Evaluation license.", e);
			return getEvaluationLicense();
		}
		License license = parseLicense(buffer.toString());
		
		return license;
	}
	
	public void saveLicense(String licenseString) {
		License license = parseLicense(licenseString);
		if(license==null){
			logger.warn("Invalid license!");
			return;
		}
		String path = BaseContext.getRootPath()+Environment.LICENSE_FILE;
		File file = new File(path);
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			FileWriter writer = new FileWriter(file);
			writer.write(licenseString);
			writer.close();
			logger.info("Write license to:", path);
		} catch (IOException e) {
			logger.error("Write license failed!", e);
		}
		BaseContext.setLicense(license);
	}

	/**
	 * 保存license到文件
	 * @param license
	 * @param path 
	 */
	public void saveLicense(License license, String path) {
		File file = new File(path);
		String licenseString = license.toBase64String();
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			FileWriter writer = new FileWriter(file);
			writer.write(licenseString);
			writer.flush();
			writer.close();
			logger.info("Write license to:", path);
		} catch (IOException e) {
			logger.error("Write license failed!", e);
		}
	}

	/**
	 * @param LicenseRegistry license申请信息
	 * @param privateKey	私钥
	 * @return 根据license申请生成数字签名,签名出现异常则返回null
	 */
	public String getSignature(LicenseRegistry licenseRegistry) {
		try {
			Signature signature = Signature.getInstance(SecurityConstant.SIGNATURE_ALGORITHM);
			signature.initSign(privateKey);
			signature.update(licenseRegistry.toString().getBytes("UTF8"));

			Base64 base64 = new Base64();
			String baseSignature = new String(base64.encode(signature.sign()), "UTF8");
			return baseSignature;
		} catch (Exception e) {
			logger.error("get license Signature failed!", e);
		}
		return null;
	}

	/**
	 * @param plainText 需要验证的文本信息
	 * @param signatureText	转换为base64字符的数字签名码
	 * @param publicKey 公钥
	 * @return 验证签名是否正确,正确则返回true
	 */
	public boolean verifySignature(String plainText, String signatureText) {
		logger.info("Verify Signature");
		try {
			Signature signature = Signature.getInstance(SecurityConstant.SIGNATURE_ALGORITHM);
			signature.initVerify(publicKey);
			signature.update(plainText.getBytes("UTF8"));

			Base64 base64 = new Base64();
			byte[] signatureBytes = base64.decode(signatureText.getBytes("UTF8"));

			return signature.verify(signatureBytes);

		} catch (Exception e) {
			logger.error("Verify license signature failed!",e);
		}
		return false;
	}

	/**
	 * 判断签名是否正确
	 */
	public boolean verifySignature(License license) {
		String plainText = license.getVerifyText();
		String signature = license.getSignature();
		return verifySignature(plainText, signature);
	}
	
	public License getEvaluationLicense(){
		LicenseRegistry licenseRegistry = new LicenseRegistry();
		licenseRegistry.setLicenseType(LicenseType.Evaluation);
		licenseRegistry.setOrganization(Environment.ORGANIZATION);
		licenseRegistry.setExpireDate("2012-12-31");
		licenseRegistry.setMaxLicense(1);
		licenseRegistry.setProductName(Environment.PRODUCT_NAME);
		licenseRegistry.setProductVersion(Version.VERSION);
		licenseRegistry.setServerID(BaseContext.getSID());
		return new License(licenseRegistry);
	}
	
	public PrivateKey getPrivateKey() {
		return privateKey;
	}

	public void setPrivateKey(PrivateKey privateKey) {
		this.privateKey = privateKey;
	}

	public PublicKey getPublicKey() {
		return publicKey;
	}

	public void setPublicKey(PublicKey publicKey) {
		this.publicKey = publicKey;
	}
}
